﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Borg
{
    public class SettingList : SettingCollection, IEnumerable<object>
    {
        protected readonly List<object> values = new List<object>();

        public int Count
        {
            get { return values.Count; }
        }

        public object this[int index]
        {
            get { return values[index]; }
            set
            {
                if (SettingStore != null && !SettingStore.CheckAccess())
                    throw RuntimeElement.GetAccessException();
                
                if (value is SettingCollection)
                    InternalInsertCollection(index, (SettingCollection)value);
                else
                    InternalInsertValue(index, value);
            }
        }

        public void AddValue<T>(T value)
        {
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalAddValue(value);
        }

        public void AddDictionary(SettingDictionary settingDictionary)
        {
            if (settingDictionary == null)
                throw new ArgumentNullException("settingDictionary");

            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalAddCollection(settingDictionary);
        }

        public void AddList(SettingList settingList)
        {
            if (settingList == null)
                throw new ArgumentNullException("settingList");
            
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();

            InternalAddCollection(settingList);
        }

        public void InsertValue<T>(int index, T value)
        {
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalInsertValue(index, value);
        }

        public void InsertDictionary(int index, SettingDictionary settingDictionary)
        {
            if (settingDictionary == null)
                throw new ArgumentNullException("settingDictionary");
            
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalInsertCollection(index, settingDictionary);
        }

        public void InsertList(int index, SettingList settingList)
        {
            if (settingList == null)
                throw new ArgumentNullException("settingList");

            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalInsertCollection(index, settingList);
        }

        public void RemoveValue<T>(T value)
        {
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalRemoveValue(value);
        }

        public void RemoveDictionary(SettingDictionary settingDictionary)
        {
            if (settingDictionary == null)
                throw new ArgumentNullException("settingDictionary");

            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalRemoveCollection(settingDictionary);
        }

        public void RemoveList(SettingList settingList)
        {
            if (settingList == null)
                throw new ArgumentNullException("settingList");
            
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalRemoveCollection(settingList);
        }

        public bool ContainsValue<T>(T value)
        {
            return InternalContainsValue(value);
        }

        public bool ContainsDictionary(SettingDictionary settingDictionary)
        {
            if (settingDictionary == null)
                throw new ArgumentNullException("settingDictionary");
            
            return InternalContainsCollection(settingDictionary);
        }

        public bool ContainsList(SettingList settingList)
        {
            if (settingList == null)
                throw new ArgumentNullException("settingList");
            
            return InternalContainsCollection(settingList);
        }

        public void Clear()
        {
            if (SettingStore != null && !SettingStore.CheckAccess())
                throw RuntimeElement.GetAccessException();
            
            InternalClear();
        }

        public IEnumerator<T> GetValues<T>()
        {
            return (from string str in values
                    select ConvertFromString<T>(str)).GetEnumerator();
        }

        public object[] ToArray()
        {
            var array = new object[values.Count];
            
            values.CopyTo(array);
            
            return array;
        }

        public IEnumerator<object> GetEnumerator()
        {
            return values.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        protected internal virtual void InternalAddValue<T>(T value)
        {
            values.Add(ConvertToString(value));
            OnCollectionChanged();
        }

        protected internal virtual void InternalAddCollection(SettingCollection collection)
        {
            values.Add(collection);
            collection.Parent = this;
            OnCollectionChanged();
        }

        protected internal virtual void InternalInsertValue<T>(int index, T value)
        {
            values.Insert(index, ConvertToString(value));
            OnCollectionChanged();
        }

        protected internal virtual void InternalInsertCollection(int index, SettingCollection collection)
        {
            values.Insert(index, collection);
            collection.Parent = this;
            OnCollectionChanged();
        }

        protected internal virtual bool InternalRemoveValue(object value)
        {
            if (!values.Remove(ConvertToString(value)))
                return false;
            
            OnCollectionChanged();
            
            return true;
        }

        protected internal virtual bool InternalRemoveCollection(SettingCollection collection)
        {
            if (!values.Remove(collection))
                return false;
            
            collection.Parent = null;
            OnCollectionChanged();
            
            return true;
        }

        protected internal virtual bool InternalContainsValue(object value)
        {
            return values.Contains(ConvertToString(value));
        }

        protected internal virtual bool InternalContainsCollection(SettingCollection collection)
        {
            return values.Contains(collection);
        }

        protected internal virtual void InternalClear()
        {
            if (values.Count == 0)
                return;

            foreach (var val in values.OfType<SettingCollection>())
                (val).Parent = null;

            values.Clear();
            OnCollectionChanged();
        }
    }
}
